﻿using Abp;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Collections.Extensions;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Json;
using Abp.Timing;
using Abp.UI;
using Castle.Core.Internal;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.WorkFlows.Dto.Manager;
using Mt.Site.Application.WorkFlows.Dto.wf;
using Mt.Site.Core.Organizations.UserCache;
using Mt.Site.Core.Organizations.UserCache.Models;
using Mt.Site.Core.WorkFlows;
using Mt.Site.Core.WorkFlows.Design;
using Mt.Site.Core.WorkFlows.Design.Entities;
using Mt.Site.Core.WorkFlows.Diy;
using Mt.Site.Core.WorkFlows.Diy.Actions;
using Mt.Site.Core.WorkFlows.Diy.Actions.Impl;
using Mt.Site.Core.WorkFlows.Enum;
using Mt.Site.Core.WorkFlows.Permissions;
using Mt.Site.Core.WorkFlows.Process;
using Mt.Site.Core.WorkFlows.Process.Entities;
using Mt.Site.Core.WorkFlows.Process.Models;
using Mt.Site.Core.WorkFlows.Process.Stores;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Mt.Site.Application.WorkFlows.Impl
{
    [AbpAuthorize(WorkFlowPermissions.Pages_Manage_WorkFlow)]
    public class WorkFlowAppService : ApplicationService, IWorkFlowAppService
    {
        private readonly IWorkFlowManager _workFlowManager;
        private readonly IRepository<WorkFlowCategeory, Guid> _workFlowCategeoryRepository;

        private readonly IWorkFlowFormDataStore _workFlowFormDataStore;
        private readonly IWorkFlowHelper _workFlowHelper;
        private readonly IIocResolver _iocResolver;

        public WorkFlowAppService(
            IWorkFlowManager workFlowManager, 
            IRepository<WorkFlowCategeory, Guid> workFlowCategeoryRepository,
            IWorkFlowFormDataStore workFlowFormDataStore,
            IWorkFlowHelper workFlowHelper,
            IIocResolver iocResolver)
        {
            _workFlowManager = workFlowManager;
            _workFlowCategeoryRepository = workFlowCategeoryRepository;
            _workFlowFormDataStore = workFlowFormDataStore; 
            _workFlowHelper = workFlowHelper;
            _iocResolver = iocResolver;
        }

        public async Task<Guid> AddNewPaperAsync(AddNewPaperInput input)
        {
            var workflow = await _workFlowManager.WorkFlowFactory.GetWorkflowDefinitionAsync(input.WorkFlowId);
            if (input.Title.IsNullOrEmpty())
            {
                input.Title = $"{workflow.Definition.Name}({Clock.Now.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")})";
            }

            await _workFlowManager.AddManuscriptAsync(workflow, input.Title);

            return _workFlowManager.PaperId;
        }

        public async Task<FormDataOutput> GetFormDataForEditAsync(EntityDto<Guid> input)
        {
            await _workFlowManager.Initialize(input.Id);
            var paper = _workFlowManager.CurrentPaper.Paper;
            var workflow = _workFlowManager.CurrentPaper.WorkFlow;

            var remarkData = await _workFlowManager.GetRemarkDataAsync();
            foreach (var item in remarkData)
            {
                item.NodeName = workflow.FindNode(item.NodeId).Definition.Name;
                item.UserInfo = item.UserInfoDtoJson.FromJsonString<UserInfoDto>();
                item.UserInfoDtoJson = "";
            }
            remarkData = remarkData.OrderBy(c => c.StepCode).ThenBy(c => c.Code).ThenBy(c => c.CreationTime)
                .ThenByDescending(c => c.UserInfo.Order).ToList();

            var actionState = await _workFlowManager.CheckActionStateAsync();
            var isBranchLine = await _workFlowManager.IsBranchLineAsync();

           
            WorkFlowProcessDto curProcessDto = await _workFlowManager.GetCurrentProcessAsync();

            string replyMsg = "";
            string defaultMsg = "";
            if (curProcessDto != null)
            {
                defaultMsg = await _workFlowManager.WorkFlowNodePluginFunction.GetDefaultMsgAsync(_workFlowManager);
                curProcessDto.UserInfo = curProcessDto.UserInfoDtoJson.FromJsonString<UserInfoDto>();
                if (curProcessDto.IsDefaultMsg || string.IsNullOrWhiteSpace(curProcessDto.Reply))
                {
                    replyMsg = defaultMsg;
                }
                else
                {
                    replyMsg = curProcessDto.Reply;
                }
            }

            var result = new FormDataOutput
            {
                Paper = ObjectMapper.Map<WorkFlowPaperDto>(paper),
                Actions = await _workFlowManager.GetActionListAsync(),
                RemarkData = remarkData,
                LogData = await _workFlowManager.GetLogDataAsync(),
                IsBranchLine = isBranchLine,
                IsMultiplePeople = (actionState == ActionState.Todo ? await _workFlowManager.IsMultiplePeopleAsync() : false),
                ActionState = actionState,
                IsFirst = _workFlowManager.IsFirst,
                HasNext = await _workFlowManager.CanGoNextAsync(),
                CanGoback = await _workFlowManager.CanGoback(),
                PreNodeList = (await _workFlowManager.GetPreAsync()).Select(c=>c.Definition.NodeId).ToList(),
                CanEditAttachment = _workFlowManager.CurrentNode.Definition.CanEditAttachment,
                IsHasAttachment = workflow.Definition.IsHaveAttachment,
                FormData = await _workFlowFormDataStore.GetFormDataForEditAsync(_workFlowManager),
                FormHtml = workflow.Definition.FormHtml,
                CanSave = actionState == ActionState.Todo,
                Now = Clock.Now,
                IsHaveOnlineWord = workflow.Definition.IsHaveOnlineWord,
                ReplyMsg = replyMsg,
                DefaultMsg = defaultMsg,
                WfUniqueName = _workFlowManager.WorkFlow.Definition.CustomData?.UniqueName,
                CurrentTask = curProcessDto,
                AdditionalMsg = await _workFlowManager.GetAllAdditionalMsg(),
                Relationships = await _workFlowManager.GetWorkFlowRelationshipsAsync()
            };

            return await _workFlowManager.WorkFlowNodePluginFunction.AlterFormDataOutput(result, _workFlowManager);
        }
        public async Task<FormDataOutput> GetDataForPrintAsync(PaperIdInput input)
        {
            await _workFlowManager.Initialize(input.PaperId);
            var paper = _workFlowManager.CurrentPaper.Paper;
            var workflow = _workFlowManager.WorkFlow;

            var remarkData = await _workFlowManager.GetRemarkDataAsync();
            foreach (var item in remarkData)
            {
                item.NodeName = workflow.FindNode(item.NodeId).Definition.Name;
                item.UserInfo = item.UserInfoDtoJson.FromJsonString<UserInfoDto>();
                item.UserInfoDtoJson = "";
            }
            remarkData = remarkData.OrderBy(c => c.StepCode).ThenBy(c => c.Code).ThenBy(c => c.CreationTime)
                .ThenByDescending(c => c.UserInfo.Order).ToList();

            return new FormDataOutput
            {
                Paper = ObjectMapper.Map<WorkFlowPaperDto>(paper),
                RemarkData = remarkData,
                LogData = await _workFlowManager.GetLogDataAsync(),
                FormData = await _workFlowFormDataStore.GetFormDataForReadOnlyAsync(paper.Id, workflow, paper.NodeId, paper.CreatorUserId.Value),
                FormHtml = workflow.Definition.FormHtml,
                Now = Clock.Now,
                IsHaveOnlineWord = workflow.Definition.IsHaveOnlineWord,
                CanEditAttachment = false,
                IsHasAttachment = workflow.Definition.IsHaveAttachment,
                WfUniqueName = _workFlowManager.WorkFlow.Definition.CustomData?.UniqueName,
                AdditionalMsg = await _workFlowManager.GetAllAdditionalMsg(),
                Relationships = await _workFlowManager.GetWorkFlowRelationshipsAsync()
            };
        }

        public async Task UpdateFormDataAsync(UpdateFormDataInput input)
        {
            await _workFlowManager.Initialize(input.Id);

            var curProcess = await _workFlowManager.GetCurrentProcessAsync();
            var actionState = await _workFlowManager.CheckActionStateAsync();
            var isBranchLine = await _workFlowManager.IsBranchLineAsync();
            
            if (actionState == ActionState.Todo)
            {
                //主办部门和批示件保存改用自定义函数
                await _workFlowManager.WorkFlowNodePluginFunction.BeforeSaveFormAsync(input, _workFlowManager);

                if (!isBranchLine && curProcess.CanEditForm)
                {
                    var paper = _workFlowManager.CurrentPaper.Paper;
                    paper.ChangeTitle(input.Paper.Title);
                    if (paper.PaperState == PaperState.Temporary)
                    {
                        paper.ChangePaperState(PaperState.Draft);
                    }

                    //保存表单数据
                    await _workFlowFormDataStore.UpdateAsync(input.Values, _workFlowManager);
                }

                await _workFlowManager.WorkFlowNodePluginFunction.AfterSaveFormAsync(input, _workFlowManager);
            }
        }

        public async Task AddOrUpdateReply(AddOrUpdateReplyInput input)
        {
            await _workFlowManager.Initialize(input.PaperId);
            await _workFlowManager.AddReplyAsync(input.ReplyMsg,input.CustomData,input.HasRead);
        }

        public async Task<GetUserSelectionDataOutput> GetUserListAsync(GetUserListInput input)
        {
            await _workFlowManager.Initialize(input.PapaerId);
            GetUserSelectionDataOutput result ;

            if (input.UnitId > 0)
            {
                result = await _workFlowManager.GetUserListForActionAsync(input.NodeId, new List<long> { input.UnitId }, input.ActionName);
            }
            else
            {
                result = await _workFlowManager.GetUserListForActionAsync(input.NodeId, null, input.ActionName);
            }

            if(result.ActionSchemeList == null)
            {
                var schemelist = new List<NameValue>();
                foreach (ActionScheme item in System.Enum.GetValues(typeof(ActionScheme)))
                {
                    var temp = new NameValue
                    {
                        Name = EnumHelper.GetEnumDescription(item),
                        Value = item.RawValue().ToString()
                    };
                    schemelist.Add(temp);
                }
                result.ActionSchemeList = schemelist;
            }
            return result;
        }

        public async Task GoBack(GoBackInput input)
        {
            await _workFlowManager.Initialize(input.PaperId);
            var isBranchLine = await _workFlowManager.IsBranchLineAsync();
            if (isBranchLine)
            {
                
            }
            else
            {
                if (input.NodeId != Guid.Empty)
                {
                    await _workFlowManager.GoBackAsync(input.NodeId,input.ReplyMsg);
                }
                else
                {
                    throw new UserFriendlyException("请选择步骤");
                }
            }
        }


        public async Task<StateWhenSubmit> CheckStateBeforeExecuteAction(ExecuteActionInput input)
        {
            await _workFlowManager.Initialize(input.PaperId);
            return await _workFlowManager.CheckStateBeforeExecuteActionAsync(input.ActionName,  null);
        }

        public async Task ExecuteAction(ExecuteActionInput input)
        {
            await _workFlowManager.Initialize(input.PaperId);
            await _workFlowManager.ExecuteActionAsync(input.ActionName, new ActionArgs
            {
                ReplyMsg = input.ReplyMsg,
                UserIdList = input.UserIdList,
                ActionScheme = input.ActionScheme,
                NodeId = input.NodeId,
                TurnRoundUserList = input.TurnRoundUserList
            });
        }

        //可拟稿的流程
        public async Task<GetWorkFlowOutput> GetWorkFlowList()
        {
            var currentUserInfo = await _workFlowManager.UserAndUnitFinder.GetCurrentUserInfoAsync();
            if (currentUserInfo == null || currentUserInfo.Units.Count == 0)
            {
                return new GetWorkFlowOutput { Categories = new List<NameValue<Guid>>(), WorkFlows = new Dictionary<Guid, List<NameValue<Guid>>>() };
            }

            var unitIds = currentUserInfo.Units.Select(c => c.Id).ToList();
            var userId = currentUserInfo.UserId;
            var categoryList = await _workFlowCategeoryRepository.GetAllListAsync(c => c.IsActive);
            var worflowList = await _workFlowManager.WorkFlowFactory.GetAllWorkFlowDefinitionAsync();
            worflowList = CheckWorkflowList(unitIds, userId, worflowList);

            var result = new GetWorkFlowOutput();
            result.Categories = worflowList
                .Select(c => c.Definition.CategeoryId)
                .Distinct()
                .Select(c => categoryList.FirstOrDefault(m => m.Id == c))
                .Where(c => c != null)
                .OrderBy(c => c.Order)
                .Select(c => new NameValue<Guid>(c.Name, c.Id))
                .ToList();
            result.WorkFlows = new Dictionary<Guid, List<NameValue<Guid>>>();
            result.Categories.ForEach(c =>
            {
                var list = worflowList
                .Where(wf => wf.Definition.CategeoryId == c.Value)
                .OrderBy(wf => wf.Definition.Order)
                .Select(wf => new NameValue<Guid>(wf.Definition.Name, wf.Definition.WorkFlowId))
                .ToList();

                result.WorkFlows.Add(c.Value, list);
            });
            return result;
        }

        private static List<IWorkflowDefinition> CheckWorkflowList(List<long> unitIds, long userId, List<IWorkflowDefinition> worflowList)
        {
            worflowList = worflowList
                .Where(c => c.Definition.IsActive && c.Definition.NodeList.Length > 0)
                .Where(c =>
                {
                    if (c.Definition.UnitIdList.IsNullOrEmpty())
                    {
                        return true;
                    }

                    foreach (var unitId in unitIds)
                    {
                        if (c.Definition.UnitIdList.Contains(unitId))
                        {
                            return true;
                        }
                    }
                    return false;
                })
                .Where(c => c.FirstNode.Definition.UserIdList.IsNullOrEmpty() || c.FirstNode.Definition.UserIdList.Contains(userId))
                .ToList();
            return worflowList;
        }

        public async Task<GetWorkFlowInfoOutput> GetWorkFlowInfoAsync(EntityDto<Guid> input)
        {
            var currentUserInfo = await _workFlowManager.UserAndUnitFinder.GetCurrentUserInfoAsync();
            if (currentUserInfo != null && currentUserInfo.Units.Count > 0)
            {
                var unitIds = currentUserInfo.Units.Select(c => c.Id).ToList();
                var userId = currentUserInfo.UserId;

                var worflowDefinition = await _workFlowManager.WorkFlowFactory.GetWorkflowDefinitionAsync(input.Id);

                var worflowList = new List<IWorkflowDefinition> { worflowDefinition };
                worflowList = CheckWorkflowList(unitIds, userId, worflowList);
                if (worflowList.Count > 0)
                {
                    //var nodes = await _workFlowNodeRepository.GetAllListAsync(item => item.IsActive && item.WorkflowId == input.Id);
                    //nodes = nodes.OrderBy(item => item.Order)
                    //            .ThenBy(item => item.CreationTime)
                    //            .ToList();
                    //var list = ObjectMapper.Map<List<WorkFlowNodeDto>>(nodes);

                    var worflowDef = worflowList[0].Definition;
                    return new GetWorkFlowInfoOutput
                    {
                        Name = worflowDef.Name,
                        ShortName = worflowDef.ShortName,
                        Id = worflowDef.WorkFlowId,
                        DefaultTitle = await _workFlowHelper.GetDefautTitle(worflowDef.WorkFlowId),
                        //NodeList = list
                    };
                }
            }
            return new GetWorkFlowInfoOutput ();
        }

        public async Task<GetNodeListOutput> GetNodeList(GetNodeListInput input)
        {
            await _workFlowManager.Initialize(input.PaperId);

            IList<IWorkflowNodeDefinition> list;
            if (input.Type == GetNodeListType.GoBack)
            {
                list = await _workFlowManager.GetPreAsync();
            }
            else if (input.Type == GetNodeListType.GoNext)
            {
                list = await _workFlowManager.GetNextAsync();
            }
            else
            {
                list = new List<IWorkflowNodeDefinition> { _workFlowManager.CurrentNode };
            }

            List<GetNodeListDto> nodeList;
            if (list.IsNullOrEmpty())
            {
                nodeList = new List<GetNodeListDto>();
            }
            else
            {
                nodeList = list.Select(c =>
                    new GetNodeListDto { Name = c.Definition.Name, value = c.Definition.NodeId, DefaultSelected = c.DefaultSelected })
                    .ToList();
            }

            return new GetNodeListOutput
            {
                NodeList = nodeList
            };
        }

        public async Task AddProcessMsg(AddProcessMsgInput input)
        {
            await _workFlowManager.Initialize(input.PaperId);
            await _workFlowManager.TryAddAdditionalMsg(input.Msg);
        }

        public async Task<List<WorkFlowProcessMsgDto>> GetAllAdditionalMsg(EntityDto<Guid> input)
        {
            await _workFlowManager.Initialize(input.Id);
            return await _workFlowManager.GetAllAdditionalMsg();
        }
    }
}
